﻿using System;
using System.Threading.Tasks;

namespace Sidvall.Devices.Uwp
{
    public class Geolocator : IGeolocator
    {
        #region Public Members

        #region DesiredAccuracy

        public PositionAccuracy DesiredAccuracy
        {
            get
            {
                return GetPositionAccuracy(this.InternalGeolocator.DesiredAccuracy);
            }
            set
            {
                this.InternalGeolocator.DesiredAccuracy = GetPositionAccuracy(value);
            }
        }

        #endregion
        #region DesiredAccuracyInMeters

        public uint? DesiredAccuracyInMeters
        {
            get
            {
                return this.InternalGeolocator.DesiredAccuracyInMeters;
            }
            set
            {
                this.InternalGeolocator.DesiredAccuracyInMeters = value;
            }
        }

        #endregion
        #region LocationStatus

        public PositionStatus LocationStatus
        {
            get
            {
                return GetPositionStatus(this.InternalGeolocator.LocationStatus);
            }
        }

        #endregion
        #region MovementThreshold

        public double MovementThreshold
        {
            get
            {
                return this.InternalGeolocator.MovementThreshold;
            }
            set
            {
                this.InternalGeolocator.MovementThreshold = value;
            }
        }

        #endregion
        #region ReportInterval

        public uint ReportInterval
        {
            get
            {
                return this.InternalGeolocator.ReportInterval;
            }
            set
            {
                this.InternalGeolocator.ReportInterval = value;
            }
        }

        #endregion

        #region GetGeopositionAsync

        public async Task<Geoposition> GetGeopositionAsync()
        {
            var item = await this.InternalGeolocator.GetGeopositionAsync();
            return GetGeoposition(item);
        }

        public async Task<Geoposition> GetGeopositionAsync(TimeSpan maximumAge, TimeSpan timeout)
        {
            var item = await this.InternalGeolocator.GetGeopositionAsync(maximumAge, timeout);
            return GetGeoposition(item);
        }

        #endregion
        #region ToString

        public override string ToString()
        {
            return "Geolocator - UWP";
        }

        #endregion

        #endregion
        #region Private Members

        private Windows.Devices.Geolocation.Geolocator InternalGeolocator { get; set; }

        #region GetGeoposition

        private Geoposition GetGeoposition(Windows.Devices.Geolocation.Geoposition position)
        {
            return new Geoposition()
            {
                Accuracy = position.Coordinate.Accuracy,
                AltitudeAccuracy = position.Coordinate.AltitudeAccuracy,
                Heading = position.Coordinate.Heading,
                Point = GetGeopoint(position.Coordinate.Point),
                PositionSource = GetPositionSource(position.Coordinate.PositionSource),
                PositionSourceTimestamp = position.Coordinate.PositionSourceTimestamp,
                Speed = position.Coordinate.Speed,
                Timestamp = position.Coordinate.Timestamp,
            };
        }

        #endregion
        #region GetAltitudeReferenceSystem

        private AltitudeReferenceSystem GetAltitudeReferenceSystem(Windows.Devices.Geolocation.AltitudeReferenceSystem system)
        {
            switch (system)
            {
                case Windows.Devices.Geolocation.AltitudeReferenceSystem.Unspecified:
                    return AltitudeReferenceSystem.Unspecified;
                case Windows.Devices.Geolocation.AltitudeReferenceSystem.Terrain:
                    return AltitudeReferenceSystem.Terrain;
                case Windows.Devices.Geolocation.AltitudeReferenceSystem.Ellipsoid:
                    return AltitudeReferenceSystem.Ellipsoid;
                case Windows.Devices.Geolocation.AltitudeReferenceSystem.Geoid:
                    return AltitudeReferenceSystem.Geoid;
                case Windows.Devices.Geolocation.AltitudeReferenceSystem.Surface:
                    return AltitudeReferenceSystem.Surface;
                default:
                    throw new System.ArgumentException(nameof(system));
            }
        }

        #endregion
        #region GetGeoshapeType

        private GeoshapeType GetGeoshapeType(Windows.Devices.Geolocation.GeoshapeType type)
        {
            switch (type)
            {
                case Windows.Devices.Geolocation.GeoshapeType.Geopoint:
                    return GeoshapeType.Geopoint;
                case Windows.Devices.Geolocation.GeoshapeType.Geocircle:
                    return GeoshapeType.Geocircle;
                case Windows.Devices.Geolocation.GeoshapeType.Geopath:
                    return GeoshapeType.Geopath;
                case Windows.Devices.Geolocation.GeoshapeType.GeoboundingBox:
                    return GeoshapeType.GeoboundingBox;
                default:
                    throw new System.ArgumentException(nameof(type));
            }
        }

        #endregion
        #region GetPositionSource

        private PositionSource GetPositionSource(Windows.Devices.Geolocation.PositionSource source)
        {
            switch (source)
            {
                case Windows.Devices.Geolocation.PositionSource.Cellular:
                    return PositionSource.Cellular;
                case Windows.Devices.Geolocation.PositionSource.Satellite:
                    return PositionSource.Satellite;
                case Windows.Devices.Geolocation.PositionSource.WiFi:
                    return PositionSource.WiFi;
                case Windows.Devices.Geolocation.PositionSource.IPAddress:
                    return PositionSource.IPAddress;
                case Windows.Devices.Geolocation.PositionSource.Unknown:
                    return PositionSource.Unknown;
                case Windows.Devices.Geolocation.PositionSource.Default:
                    return PositionSource.Default;
                case Windows.Devices.Geolocation.PositionSource.Obfuscated:
                    return PositionSource.Obfuscated;
                default:
                    throw new System.ArgumentException(nameof(source));
            }
        }

        #endregion
        #region GetGeopoint

        private Geopoint GetGeopoint(Windows.Devices.Geolocation.Geopoint point)
        {
            return new Geopoint()
            {
                Altitude = point.Position.Altitude,
                AltitudeReferenceSystem = GetAltitudeReferenceSystem(point.AltitudeReferenceSystem),
                GeoshapeType = GetGeoshapeType(point.GeoshapeType),
                Latitude = point.Position.Latitude,
                Longitude = point.Position.Longitude,
                SpatialReferenceId = point.SpatialReferenceId,

            };
        }

        #endregion
        #region GetPositionStatus

        private PositionStatus GetPositionStatus(Windows.Devices.Geolocation.PositionStatus status)
        {
            switch (status)
            {
                case Windows.Devices.Geolocation.PositionStatus.Ready:
                    return PositionStatus.Ready;
                case Windows.Devices.Geolocation.PositionStatus.Initializing:
                    return PositionStatus.Initializing;
                case Windows.Devices.Geolocation.PositionStatus.NoData:
                    return PositionStatus.NoData;
                case Windows.Devices.Geolocation.PositionStatus.Disabled:
                    return PositionStatus.Disabled;
                case Windows.Devices.Geolocation.PositionStatus.NotInitialized:
                    return PositionStatus.NotInitialized;
                case Windows.Devices.Geolocation.PositionStatus.NotAvailable:
                    return PositionStatus.NotAvailable;
                default:
                    throw new System.ArgumentException(nameof(status));
            }
        }

        #endregion
        #region GetPositionAccuracy

        private PositionAccuracy GetPositionAccuracy(Windows.Devices.Geolocation.PositionAccuracy accuracy)
        {
            switch (accuracy)
            {
                case Windows.Devices.Geolocation.PositionAccuracy.Default:
                    return PositionAccuracy.Default;
                case Windows.Devices.Geolocation.PositionAccuracy.High:
                    return PositionAccuracy.High;
                default:
                    throw new System.ArgumentException(nameof(accuracy));
            }
        }
        private Windows.Devices.Geolocation.PositionAccuracy GetPositionAccuracy(PositionAccuracy accuracy)
        {
            switch (accuracy)
            {
                case PositionAccuracy.Default:
                    return Windows.Devices.Geolocation.PositionAccuracy.Default;
                case PositionAccuracy.High:
                    return Windows.Devices.Geolocation.PositionAccuracy.High;
                default:
                    throw new System.ArgumentException(nameof(accuracy));
            }
        }

        #endregion

        #endregion
        #region Constructors

        public Geolocator(Windows.Devices.Geolocation.Geolocator geolocator)
        {
            this.InternalGeolocator = geolocator;
        }

        #endregion
    }
}
